package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class UdpEchoServer {
    //UdpEchoServer回显服务器
        //对于服务器来说首先要创建DatagramSocket对象
        //接下来操作网卡都是通过socket对象完成的
        //Socket再内存中却能操作网卡此对象相当于一个遥控器,操作此对象就可以操作网卡
    private DatagramSocket socket = null;
    //throws SocketException此异常是我们网络编程中常见的异常,再端口请求失败的情况下就会出现此异常
    public UdpEchoServer(int port) throws SocketException {
        //用服务器的构造方法将端口号传入socket中
        //此程序已启动就需要关联上一个操作系统的端口号
        //端口号是一个整数,用来区分一个主机上网络通信的程序,任务
        //QQ,CCtalk等启动都会绑定一个端口号,但是一个进程可以绑定多个端口
        //端口号和socket对象是一一对应的,一个进程创建多个socket对象自然也可以绑定多个端口
        //此时我们构造方法是创建是由程序员手动指定端口号

        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        //服务器的启动逻辑,后续可以通过调用start使服务器启动
        System.out.println("服务器启动!!");
        //对于服务器来说需要不停的收到请求返回响应....
        //一个服务器单位时间能处理的请求越多,返回的响应越多,这个服务器就越厉害
        while(true){
            //由于服务器大部分是无时无刻运行,一般不需要退出
            //若是要退出就直接杀进程就可以了,服务器通常运行再linux系统上,而linux结束进程就是kill命令
            //我们的每次循环就是处理一个请求 - 响应
            //1,读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[2096],2096);
            //此时就相当于设定了一个字节数组,可以保存应用层数据包的信息  长度就是给Packet分配的空间
            socket.receive(requestPacket);
            //网卡上读写也叫输入输出,所以会抛出IO异常
            //此时receive就可以从网卡中读取UDP数据报,数据报的载荷部分就被放入数组当中去了
            //另外他的报头部分也会被socket的其他属性保存,包括其他的信息就都被保存好了(源ip,源端口)
            //DatagramPacket也是一个"输出型参数"实际上此类型内部就会包含一个字节数组

            //将读到的字节数组构造成字符串,以便处理 , getData得到的字节数组就是刚才传入的数组
            String s = new String(requestPacket.getData(), 0 , requestPacket.getLength());
            //2.根据请求计算响应(对于回显服务器来说这一步什么都不用做)
            String response = process(s);
            //3.把响应返回到客户端
            //构造一个DatagramPacket对象构成响应
            DatagramPacket responsePacket = new DatagramPacket(s.getBytes(StandardCharsets.UTF_8),s.getBytes().length,
                    requestPacket.getSocketAddress());//得到一个INetAddress对象,这个对象就包含了ip和端口
            //把请求中的源ip源端口作为返回时的目的ip和目的端口.
            //上述代码可以看出UDP是无通信连接~~UDP不会保存对端的IP和端口
            //代码中也没有建立连接和接收连接的操作,就是发送接收
            //全双工  一个socket既可以去接收也可以发送
            socket.send(responsePacket);

            //打印日志
            System.out.printf("[%s:%d]req: %s, resp: %s\n",responsePacket.getAddress().toString()
            ,requestPacket.getPort(),s,response);
        }
    }
    public String process(String s){
        return s;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}
